import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;

public class TFTP
{
	protected int tamanoBuffer;
	private int tamData = 512;
	protected byte[] buffer;
	
	private DatagramSocket socketCliente;
	private DatagramSocket socketServidor;
	
	protected DatagramPacket paquete;
	
	protected InetAddress direccionCliente;
	protected int puertoCliente;// = 12344;
	
	protected InetAddress direccionServidor;
	protected int puertoServidor;// = 12345;
	
	protected boolean soyServidor;
	protected boolean conexionEstablecida = false;
	
	private int numBloqueServidor = 0;
	private int numBloqueCliente = 0;
	private int tipoError = 0;
	protected int siguienteTipoPaquete=0;
	
	private boolean modoVerbose = false;
	
	private int contadorErroresCRC=0;
	private int contadorErroresPaquetes=0;
	
	FileInputStream fis = null;
	BufferedInputStream bis = null;
	FileOutputStream fos = null;
	BufferedOutputStream bos = null;
	
	//Para calcular la velocidad media
	Date now = new Date();
    long inicioTiempo,finalTiempo;
	
	File archivoSalida;
	FileWriter escritorArchivo = null;
	BufferedWriter bufferEscritor = null;
	BufferedReader bufferLector = null;
	int bytesLeidos = 0;
	
	boolean modoTrace = false;
	public boolean transferenciaAcabada = false;
	private BufferedWriter archivoTrace;
	
	//variables para perdidas de paquetes
	private boolean hayErroresPaquete;
	private int porcentajeErroresPaquete;
	
	//variables para errores de CRC
	private boolean hayErroresCRC;
	private int porcentajeErroresCRC;
	
	public TFTP(int tamanoBuffer, int puerto, int seleccion) throws SocketException, UnknownHostException {
		super();
		this.tamanoBuffer = tamanoBuffer;		
		buffer = new byte[tamanoBuffer];
	}
	
	public TFTP(boolean soyServidor)
	{
		this.soyServidor = soyServidor;
		tamanoBuffer = 600;
		buffer = new byte[tamanoBuffer];
	}
	
	public void ponerParametros(boolean modoVerbose, boolean modoTrace,
			boolean hayPerdidaPaquetes, int	porcentajeErroresPaquete,
			boolean hayErroresCRC, int porcentajeErroresCRC)
	{
		this.modoVerbose = modoVerbose;
		if(modoTrace == true)
		{
			this.modoTrace = true;
			try {
				archivoTrace = new BufferedWriter(new FileWriter("trace.txt"));
			} catch (IOException e) {
				System.out.println("No se pudo crear el archivo trace.txt");
			}
		}
		this.hayErroresPaquete = hayPerdidaPaquetes;
		this.hayErroresCRC = hayErroresCRC;
		this.porcentajeErroresPaquete = porcentajeErroresPaquete;
		this.porcentajeErroresCRC = porcentajeErroresCRC;
		
	}
	
	public void setTrace()
	{
		modoTrace = true;
		try {
			archivoTrace = new BufferedWriter(new FileWriter("trace.txt"));
		} catch (IOException e) {
			System.out.println("No se pudo crear el archivo trace.txt");
		}
	}
	
	public void setVerbose()
	{
		modoVerbose = true;
	}
	
	public int getNumBloque()
	{
		if(soyServidor)
			return numBloqueServidor;
		else
			return numBloqueCliente;
	}
	
	protected String getIPPublica()
	{
		BufferedReader buffer = null;
		String line = "";
        try {
                URL url = new URL("http://whatismyip.com/automation/n09230945.asp");
                InputStreamReader in = new InputStreamReader(url.openStream());
                buffer = new BufferedReader(in);

                line = buffer.readLine();
                //System.out.println(line);
        } catch (IOException e) {
                e.printStackTrace();
        } finally {
                try {
                        if (buffer != null) {
                                buffer.close();
                        }
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
        return line;
	}
	
	protected void abrirSocket()
	{
		try
		{	
			if(soyServidor)
			{
				socketServidor = new DatagramSocket(puertoServidor, InetAddress.getLocalHost());
				//socketServidor.setSoTimeout(10000);
			}
			else
			{
				socketCliente = new DatagramSocket(puertoCliente, InetAddress.getLocalHost());
				//socketCliente.setSoTimeout(10000);
			}
			
			if(modoTrace)
				archivoTrace.write(getHoraActual()+" Socket creado correctamente."+System.getProperty("line.separator"));
			
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (Exception e){
			e.printStackTrace();
		}
	}
	
	protected void cerrarSocket()
	{
		if(soyServidor)
			socketServidor.close();
		else
			socketCliente.close();
		try{
		if(modoTrace){
			archivoTrace.write(getHoraActual()+" Socket cerrado correctamente."+System.getProperty("line.separator"));
			archivoTrace.close();
		}
		} catch (IOException e){}
	}
	
	protected void setIPCliente(String IPString)
	{
		try {
			direccionCliente = InetAddress.getByName(IPString);
		} catch (UnknownHostException e) {
			System.err.println("No existe el host");
			e.printStackTrace();
		}		
	}
	
	protected void setIPServidor(String IPString)
	{
		try {
			direccionServidor = InetAddress.getByName(IPString);
		} catch (UnknownHostException e) {
			System.err.println("No existe el host");
			e.printStackTrace();
		}		
	}
	
	protected void setPuertoCliente(String puertoCliente)
	{
		this.puertoCliente = Integer.parseInt(puertoCliente);
	}
	
	protected void setPuertoServidor(String puertoServidor)
	{
		this.puertoServidor = Integer.parseInt(puertoServidor);
	}
	
	protected int getTipoError()
	{
		return tipoError;
	}
	
	protected int getSiguienteTipoPaquete()
	{
		return siguienteTipoPaquete;
	}
	
	protected void setSiguientePaquete(int tipo)
	{
		siguienteTipoPaquete = tipo;
		if(tipo==0)
		{
			System.out.println("Elige una opcion valida.");
			transferenciaAcabada = true;
		}
	}
	
	protected boolean transferenciaAcabada()
	{
		return transferenciaAcabada;
	}
	
	protected void setTimeout(int time)
	{
		try {
		if(soyServidor)
			socketServidor.setSoTimeout(time);
		else
			socketCliente.setSoTimeout(time);
	} catch (SocketException e) {
		e.printStackTrace();
	}
	}
	
	private boolean errorComprobarArchivo(boolean lectura, String nombreArchivo)
	{
		File archivo = new File(nombreArchivo);
		boolean salida=false;
		if(soyServidor && lectura && !archivo.exists())
		{
			tipoError = 1;
			salida = true;
		}
		if(soyServidor && !lectura && archivo.exists())
		{
			tipoError = 6;
			salida = true;
		}
		if(!soyServidor && !lectura && archivo.exists())
		{
			tipoError = 6;
			salida = true;
		}
		
		return salida;
	}
	
	private int arr2int(byte[] arr, int start) {
		int low = arr[start] & 0xff;
		int high = arr[start+1] & 0xff;
		int valor = (int)( high << 8 | low ); 
		return Math.abs(valor);
	}

	
	private byte[] shortABytes(short s) {
        return new byte[]{(byte)(s & 0x00FF),(byte)((s & 0xFF00)>>8)};
    }
	
	protected void desempaquetar()
	{
		byte[] opCode = new byte[2];
		byte[] arrayData;
		opCode[0] = buffer[0];
		opCode[1] = buffer[1];
		byte[] numBloque = new byte[2];
		int numBloqueInt, fin;
		int inicio = 2;
		int longitudCRC = 2;
		byte[] cadenaChar;
		int codigo = arr2int(opCode, 0);
		String cadena;
		short crcShort;
		short crcShortCalculado;
		
		switch(codigo)
		{
		case 1:	//PAQUETE RRQ
			fin = inicio;
			while(buffer[fin] != 0)
			{
				fin++;
			}
			cadenaChar = new byte[fin-inicio];
			System.arraycopy(buffer, inicio,cadenaChar,0,fin-inicio);
			cadena = new String(cadenaChar);
			
			if(errorComprobarArchivo(true,cadena))
				siguienteTipoPaquete = 5;
			else
			{
				try {
					fis = new FileInputStream(cadena);
				bis = new BufferedInputStream(fis);
				numBloqueServidor = 1;
				siguienteTipoPaquete = 3;
				} catch (FileNotFoundException e) {
					
				}
			}

			inicio = fin+1;
			fin = inicio;
			while(buffer[fin] != 0)
			{
				fin++;
			}
			cadenaChar = new byte[fin-inicio];
			System.arraycopy(buffer, inicio,cadenaChar,0,fin-inicio);
			cadena = new String(cadenaChar);
			break;
			
		case 2:	//PAQUETE WRQ
			fin = inicio;
			while(buffer[fin] != 0)
			{
				fin++;
			}
			cadenaChar = new byte[fin-inicio];
			System.arraycopy(buffer, inicio,cadenaChar,0,fin-inicio);
			cadena = new String(cadenaChar);
			
			if(errorComprobarArchivo(false,cadena))
				siguienteTipoPaquete = 5;
			else
			{
				try {
					fos = new FileOutputStream(cadena);
					bos = new BufferedOutputStream(fos);
				} catch (FileNotFoundException e1) {
					siguienteTipoPaquete = 5;
					tipoError = 2;
				}
				
				numBloqueServidor = 0;
				siguienteTipoPaquete = 4;
			}
						
			inicio = fin+1;
			fin = inicio;
			while(buffer[fin] != 0)
			{
				fin++;
			}
			cadenaChar = new byte[fin-inicio];
			System.arraycopy(buffer, inicio,cadenaChar,0,fin-inicio);
			cadena = new String(cadenaChar);
			break;
			
		case 3:	//PAQUETE DATA
			numBloque[0] = buffer[2];
			numBloque[1] = buffer[3];
			byte[] crcBytes = new byte[2];
			numBloqueInt = arr2int(numBloque, 0);
			if(soyServidor)
				numBloqueServidor = numBloqueInt;
			else
				numBloqueCliente = numBloqueInt;
			
			if(modoVerbose)
				System.out.println("Numero de bloque leido (en DATA): "+numBloqueInt);
			inicio = 4;
			
			try {
				if(modoTrace)
					archivoTrace.write(getHoraActual()+" Numero de bloque leido (en DATA): "+numBloqueInt+System.getProperty("line.separator"));
				inicio = 4;
				arrayData = new byte[tamData];
				arrayData = java.util.Arrays.copyOfRange(buffer, inicio, paquete.getLength()-longitudCRC);
				
				if(modoVerbose){
					System.out.println("arrayData length: "+arrayData.length);
					System.out.println("Buffer["+(paquete.getLength()-2)+"]: "+buffer[paquete.getLength()-2]+"  Buffer["+(paquete.getLength()-1)+"]: "+buffer[paquete.getLength()-1]);
				}
				if(modoTrace){
					archivoTrace.write(getHoraActual()+" arrayData length: "+arrayData.length+"\n");
					archivoTrace.write(getHoraActual()+" Buffer["+(paquete.getLength()-2)+"]: "+buffer[paquete.getLength()-2]+"  Buffer["+(paquete.getLength()-1)+"]: "+buffer[paquete.getLength()-1]+"\n");
				}
				
				crcShortCalculado = crc(arrayData);
				crcBytes = shortABytes(crcShortCalculado);
				byte[] crcBytesPaquete = new byte[2];
				crcBytesPaquete[0] = buffer[paquete.getLength()-2];
				crcBytesPaquete[1] = buffer[paquete.getLength()-1];
				boolean CRCCorrecto = false;
				
				if(crcBytesPaquete[0] == crcBytes[0])
					if(crcBytesPaquete[1] == crcBytes[1])
						CRCCorrecto = true;
				
				if(paquete.getLength() < tamData)
				{
					CRCCorrecto = true;
					if(soyServidor)
					{
						transferenciaAcabada = true;
					}
				}
				
				if(CRCCorrecto)
				{
					bos.write(arrayData,0,arrayData.length);
				}

			} catch (IOException e1) {
				e1.printStackTrace();
			}

			try {
				if(soyServidor)
				{
					numBloqueServidor = numBloqueInt;
					if(paquete.getLength()-inicio < tamData)
					{
						transferenciaAcabada = true;
						tipoError = 0;
						siguienteTipoPaquete = 4;
						bos.close();
					}
				}
				else
				{
					numBloqueCliente = numBloqueInt;
					siguienteTipoPaquete = 4;
					if(paquete.getLength()-inicio-longitudCRC < tamData)
					{
						transferenciaAcabada = true;
						tipoError = 0;
						siguienteTipoPaquete = 4;
						bos.close();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
	
		case 4:	//PAQUETE ACK
			numBloque[0] = buffer[2];
			numBloque[1] = buffer[3];
			numBloqueInt = arr2int(numBloque, 0);
			
			if(modoVerbose)
				System.out.println("Numero de bloque leido: "+numBloqueInt);
			if(modoTrace)
				try{
				archivoTrace.write(getHoraActual()+" Numero de bloque leido: "+numBloqueInt+System.getProperty("line.separator"));
				} catch (IOException e){}
			
			if(soyServidor)
			{
				if(numBloqueInt == numBloqueServidor)
				{
					numBloqueServidor++;
					siguienteTipoPaquete = 3;
				}
			}
			else
			{
				if(numBloqueInt == numBloqueCliente)
				{
					numBloqueCliente++;
					siguienteTipoPaquete = 3;
				}
			}
			break;
			
		case 5:	//PAQUETE ERROR
			byte[] numError = new byte[2];
			numError[0] = buffer[2];
			numError[1] = buffer[3];
			int numErrorInt = arr2int(numError, 0);
			String descripcionError = "";
			switch(numErrorInt)
			{
			case 1:
				descripcionError = "Archivo no encontrado";
				break;
			case 2:
				descripcionError = "Violacion de acceso";
				break;
			case 3:
				descripcionError = "Disco o buffer lleno";
				break;
			case 4:
				descripcionError = "Operacion TFTP ilegal";
				break;
			case 5:
				descripcionError = "TransferID desconocido";
				break;
			case 6:
				descripcionError = "Archivo ya existente";
				break;
			case 7:
				descripcionError = "Usuario ya existente";
				break;
			default:
				descripcionError = "No definido";
				break;
			}
			System.out.println("Numero de error leido: "+numErrorInt+"\t"+descripcionError);
			transferenciaAcabada = true;
			
			if(modoTrace)
				try{
				archivoTrace.write(getHoraActual()+" Numero de error leido: "+numErrorInt+"\t"+descripcionError+System.getProperty("line.separator"));
				} catch (IOException e){}
			
			break;
		}
	}

	protected void prepararPaquete(int tipo) throws IOException
	{
		Scanner sc = new Scanner(System.in);
		int longitudDelBuffer = 0;
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] opCode = new byte[2];
		byte[] numBloque = new byte[2];
		byte[] crcBytes = new byte[2];
		byte[] nombreArchivo;
		byte[] modo;
		byte zero = 0;
		String cadena;
		short crcShort;
		
		switch(tipo)
		{
		case 1:	//Paquete tipo RRQ
			baos = new ByteArrayOutputStream();
			//CODIGO OPERADOR
			opCode = shortABytes((short)1);
			baos.write(opCode);
			longitudDelBuffer = longitudDelBuffer + opCode.length;
			
			//NOMBRE DEL ARCHIVO
			System.out.print("Escribe el nombre del archivo: ");
			cadena = sc.nextLine();
			if(!errorComprobarArchivo(false, cadena))
			{
				nombreArchivo = cadena.getBytes();
				baos.write(nombreArchivo, 0, nombreArchivo.length);
				longitudDelBuffer = longitudDelBuffer + nombreArchivo.length;
				try {
					fos = new FileOutputStream(cadena);
					bos = new BufferedOutputStream(fos);
				} catch (FileNotFoundException e1) {
					siguienteTipoPaquete = 5;
					tipoError = 2;
				}
			}
			
			//BYTE CERO
			baos.write(zero);
			longitudDelBuffer++;
			
			//MODO DE TRANSFERENCIA
			cadena = "binary";
			modo = cadena.getBytes();
			baos.write(modo, 0, modo.length);
			longitudDelBuffer = longitudDelBuffer + modo.length;
			
			//BYTE CERO
			baos.write(zero);
			longitudDelBuffer++;
			break;
			
		case 2:	//Paquete tipo WRQ
			baos = new ByteArrayOutputStream();
			//CODIGO OPERADOR
			opCode = shortABytes((short)2);
			baos.write(opCode);
			longitudDelBuffer = longitudDelBuffer + opCode.length;
			
			//NOMBRE DEL ARCHIVO
			System.out.print("Escribe el nombre del archivo: ");
			cadena = sc.nextLine();
			try{
			fis = new FileInputStream(cadena);
			bis = new BufferedInputStream(fis);
			}catch (FileNotFoundException e1) {
				System.out.println("No se encuentra el archivo");
				siguienteTipoPaquete = 0;
				transferenciaAcabada = true;
				break;
			}
			nombreArchivo = cadena.getBytes();
			baos.write(nombreArchivo, 0, nombreArchivo.length);
			longitudDelBuffer = longitudDelBuffer + nombreArchivo.length;

			//BYTE CERO
			baos.write(zero);
			longitudDelBuffer++;
			
			//MODO DE TRANSFERENCIA
			cadena = "binary";
			modo = cadena.getBytes();
			baos.write(modo, 0, modo.length);
			longitudDelBuffer = longitudDelBuffer + modo.length;
			
			//BYTE CERO
			baos.write(zero);
			longitudDelBuffer++;
			
			numBloqueCliente = 0;
			
			//Fragmento para empezar a contar el tiempo
			inicioTiempo = System.currentTimeMillis();
			break;
			
		case 3:	//Paquete tipo DATA
			baos = new ByteArrayOutputStream();
			//CODIGO OPERADOR
			opCode = shortABytes((short)3);
			baos.write(opCode);
			longitudDelBuffer = longitudDelBuffer + opCode.length;
			
			//NUMERO DE BLOQUE
			if(soyServidor)
				numBloque = shortABytes((short)this.numBloqueServidor);
			else
				numBloque = shortABytes((short)this.numBloqueCliente);
			baos.write(numBloque,0,numBloque.length);
			longitudDelBuffer = longitudDelBuffer + numBloque.length;
			
			//DATOS
			byte[] data = new byte[tamData];
			bytesLeidos = bis.read(data);
			baos.write(data,0,bytesLeidos);
			longitudDelBuffer = longitudDelBuffer + bytesLeidos;
			
			//CRC
			crcShort = crc(data);
			crcBytes = shortABytes(crcShort);
			if(modoVerbose)
				System.out.println("CRCBytes[0]: "+crcBytes[0]+"  CRCBytes[1]: "+crcBytes[1]);
			baos.write(crcBytes,0,crcBytes.length);
			longitudDelBuffer = longitudDelBuffer + crcBytes.length;
			
			if(bytesLeidos > 0 && bytesLeidos < tamData)
			{
				transferenciaAcabada = true;
				bis.close();
				//Fragmento para controlar el final de tiempo
				finalTiempo = System.currentTimeMillis();
				double tiempoCalculado = (finalTiempo - inicioTiempo);
				tiempoCalculado = tiempoCalculado / 1000;
				double numeroKBytes = (tamData*numBloqueCliente + bytesLeidos)/1024;
				double resultado = numeroKBytes / tiempoCalculado;
				DecimalFormat formateador = new DecimalFormat("####.##");
				System.out.println("Velocidad media: "+formateador.format(resultado)+" KB/s");
			}
			break;
			
		case 4:	//Paquete tipo ACK
			baos = new ByteArrayOutputStream();
		
			opCode = new byte[2];
			opCode = shortABytes((short)4);
			baos.write(opCode);
			longitudDelBuffer = longitudDelBuffer + opCode.length;
			
			if(soyServidor)
				numBloque = shortABytes((short)this.numBloqueServidor);
			else
				numBloque = shortABytes((short)this.numBloqueCliente);
			baos.write(numBloque,0,numBloque.length);
			longitudDelBuffer = longitudDelBuffer + numBloque.length;
			break;
			
		case 5:	//Paquete tipo ERROR
			baos = new ByteArrayOutputStream();
		
			opCode = new byte[2];
			opCode = shortABytes((short)5);
			baos.write(opCode);
			longitudDelBuffer = longitudDelBuffer + opCode.length;
					
			numBloque = shortABytes((short)tipoError);
			baos.write(numBloque,0,numBloque.length);
			longitudDelBuffer = longitudDelBuffer + numBloque.length;
			
			String descripcionError = "";
			switch(tipoError)
			{
			case 1:
				descripcionError = "Archivo no encontrado";
				break;
			case 2:
				descripcionError = "Violacion de acceso";
				break;
			case 3:
				descripcionError = "Disco o buffer lleno";
				break;
			case 4:
				descripcionError = "Operacion TFTP ilegal";
				break;
			case 5:
				descripcionError = "TransferID desconocido";
				break;
			case 6:
				descripcionError = "Archivo ya existente";
				break;
			case 7:
				descripcionError = "Usuario ya existente";
				break;
			default:
				descripcionError = "No definido";
				break;
			}
			byte[] arrayDescError = descripcionError.getBytes();
			baos.write(arrayDescError, 0, arrayDescError.length);
			longitudDelBuffer = longitudDelBuffer + arrayDescError.length;
			
			tipoError = 0;
			break;
		}
		this.paquete = new DatagramPacket(baos.toByteArray(),longitudDelBuffer);
	}
	
	protected void enviarPaqueteUDP()
	{
		double aleatorio = Math.random() * 100;
		int enteroAleatorio = (int)aleatorio;
		System.out.println("NUMERO ALEATORIO: "+aleatorio);
		try {
		if(aleatorio>porcentajeErroresPaquete)
		{
			if(soyServidor)
			{
				this.paquete.setAddress(direccionCliente);
				this.paquete.setPort(puertoCliente);
				socketServidor.send(paquete);
				if(siguienteTipoPaquete == 5)
					siguienteTipoPaquete = 0;
				if(modoTrace)
					archivoTrace.write(getHoraActual()+" Paquete enviado a "+paquete.getAddress()+System.getProperty("line.separator")+
							"La tasa de error fue: "+aleatorio+System.getProperty("line.separator"));
			}else{
				this.paquete.setAddress(direccionServidor);
				this.paquete.setPort(puertoServidor);
				socketCliente.send(paquete);
				if(modoTrace)
					archivoTrace.write(getHoraActual()+" Paquete enviado a "+paquete.getAddress()+System.getProperty("line.separator")+
							"La tasa de error fue: "+aleatorio+System.getProperty("line.separator"));
			}
		}
		else
		{
			try{
			if(soyServidor)
				socketServidor.setSoTimeout(1000);
			else
				socketCliente.setSoTimeout(1000);
			}
			catch(SocketException soc)
			{
				System.out.println("Salto el tiempo de socket");
			}
		}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	protected void recibirPaqueteUDP() throws IOException
	{
		BufferedOutputStream bufferedOutput = null;
		java.util.Arrays.fill(buffer,(byte) 0);
        
        try {
        	
        	paquete = new DatagramPacket(buffer, buffer.length);
        	
        	if(soyServidor)
        	{
	        	socketServidor.receive(paquete);
        	}
        	else
        		socketCliente.receive(paquete);
        }
        	catch(SocketTimeoutException e)
            {
        		System.out.println("SALTA EL DE RECIBIR");
        		System.out.println("Se vuelve a enviar el paquete anterior");
         	   contadorErroresCRC++;
         	   if(contadorErroresCRC>=10)
         		   cerrarConexion();
            };
        try{	
        	if(soyServidor)
        	{
        		if(modoVerbose)
        			System.out.println("Longitud del paquete recibido: "+paquete.getLength());
        		if(modoTrace)
        			archivoTrace.write("\nLongitud del paquete recibido: "+paquete.getLength()+System.getProperty("line.separator"));
                direccionCliente = paquete.getAddress();
                puertoCliente = paquete.getPort();
        	}
        	if(modoVerbose)
        		System.out.println("Paquete recibido de "+paquete.getAddress().getHostAddress()+":"+paquete.getPort());
        	if(modoTrace)
        		archivoTrace.write("Paquete recibido de "+paquete.getAddress().getHostAddress()+":"+paquete.getPort()+System.getProperty("line.separator"));
        	
        	desempaquetar();
        } finally {
         //Close the BufferedOutputStream
         try {
             if (bufferedOutput != null) {
                 bufferedOutput.flush();
                 bufferedOutput.close();
             }
         } catch (IOException ex) {
             ex.printStackTrace();
         }
	}
	}
	
	private short crc(byte[] cadena) { 

        int[] table = {
            0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
            0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
            0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
            0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
            0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
            0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
            0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
            0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
            0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
            0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
            0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
            0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
            0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
            0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
            0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
            0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
            0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
            0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
            0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
            0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
            0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
            0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
            0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
            0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
            0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
            0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
            0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
            0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
            0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
            0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
            0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
            0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
        };
        
        int crc = 0x0000;
        for (byte b : cadena) {
            crc = (crc >>> 8) ^ table[(crc ^ b) & 0xff];
        }
        if(modoVerbose)
        	System.out.println("CRC16 (Hex)= " + Integer.toHexString(crc));
        return (short) crc;
    }
	
	private void cerrarConexion() throws IOException
	{
		System.out.println("SE CIERRA EL SOCKET, HUBO DEMASIADOS FALLOS");
  	   cerrarSocket();
  	   if(modoTrace)
  		   archivoTrace.close();
  	   System.exit(0);
	}
	
	private String getHoraActual()
	{
		String cadena="";
		
		Calendar cal = new GregorianCalendar();

		int hour24 = cal.get(Calendar.HOUR_OF_DAY);     // 0..23
		int min = cal.get(Calendar.MINUTE);             // 0..59
		int sec = cal.get(Calendar.SECOND);             // 0..59

		cadena = "["+hour24+":"+min+":"+sec+"]";
		return cadena;
	}
}